﻿using System;
using System.Linq;
using System.Xml.Linq;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Reflection;
using System.Net.Mime;
using System.Xml;
using System.Text.RegularExpressions;
using System.Threading;
using System.Net;
using System.Collections;
using System.Diagnostics;

namespace SerialManager
{
    public enum ExitCodes
    {
        UnknownError = -1,
        Success = 0,
        NothingToDo = 1,
        MissingArguments = 50,
    }

    internal class Core
    {
        internal readonly static string[] VideoExtensions = new string[] { "avi", "mpg", "wmv" };
        internal readonly static string[] SubtitleExtensions = new string[] { "srt", "sub", "txt" };
        internal readonly static Regex[] EpisodeNumbers = new Regex[] {
            new Regex("(?<s>[0-9]{1,2})[-x](?<e>[0-9]{1,2})", RegexOptions.Singleline | RegexOptions.Compiled),
            new Regex("(?<s>[0-9]?[1-9])(?<e>[0-9]{2})", RegexOptions.Singleline | RegexOptions.Compiled),
            new Regex("[sS](?<s>[0-9]?[1-9])[eE](?<e>[0-9]{2})", RegexOptions.Singleline | RegexOptions.Compiled),
        };

        /// <summary>
        /// Klavesa pro preruseni nacitani informaci o epizodach
        /// </summary>
        internal readonly static ConsoleKey AbortKey = ConsoleKey.A;

        /// <summary>
        /// Nazev serialu (zadano pres parametry prikazove radky)
        /// Odpovida nazvu podadresare aktualniho pracovniho adresare
        /// </summary>
        public static string SerialTitle { get; private set; }

        /// <summary>
        /// Kompletni adresar k prave zpracovavanemu serialu
        /// </summary>
        public static DirectoryInfo SerialDirectory { get; private set; }

        private static Thread _sThread;
        private static Thread _bThread;

        /// <summary>
        /// Vstupni bod aplikace
        /// </summary>
        /// <param name="args"></param>
        static void Main( string[] args )
        {
            if( args.Length == 0 )
                WriteHelp();

            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhandledException);

            SerialTitle = args[ 0 ];

            SerialDirectory = new DirectoryInfo( Path.Combine( Environment.CurrentDirectory, SerialTitle ) );
            if( !SerialDirectory.Exists )
                WriteFatalError( "Directory with serial ({0}) wasn't found", SerialDirectory );

            Console.WriteLine( "Directory: {0}", SerialDirectory );

            var files = SerialDirectory.GetFiles( "*.*", SearchOption.AllDirectories );
            if( files.Length == 0 )
                WriteFatalError( "Directory with serial ({0}) is empty", SerialDirectory );

            Console.WriteLine( "Found files: {0}", files.Length );
            Console.WriteLine();

            var builder = FileListBuilder.CreateAndStartThread( files, out _bThread );

            var serial = Serial.CreateAndStartThread( SerialTitle, out _sThread );
            Thread.Sleep( 50 ); // mala pauza, aby mohl thread zacit zpracovani informaci

            DateTime timeout = DateTime.Now.AddSeconds( 60.0 );
            while( _sThread.IsAlive )
            {
                // vyhledavaci vlakno zrejme neodpovida
                if( DateTime.Now > timeout )
                {
                    Console.WriteLine( " timeout" );
                    _sThread.Abort();
                    break;
                }

                if( Console.KeyAvailable )
                {
                    var key = Console.ReadKey( true );
                    if( key.Key == AbortKey )
                    {
                        Console.WriteLine( " user aborted" );
                        _sThread.Abort();
                        break;
                    }
                }

                if( !_bThread.IsAlive && ( builder.Status != FileListBuilderStatus.BuildingFinished || builder.FoundFiles.Count == 0 ) )
                {
                    _sThread.Abort();
                    Console.WriteLine( " aborted" );
                    WriteError( "No files to process were found" );
                    Exit(ExitCodes.NothingToDo);
                }

                MakeProgress();
            }

            _sThread.Join( 1 );
            if( serial.Finished )
            {
                Console.WriteLine( " done" );
                Console.WriteLine( "Found information about {0} episodes in {1} seasons", serial.Episodes.Count, serial.SeasonCount );
                Console.WriteLine();
            }
            else
            {
                #if DEBUG
                if( serial.Error != null )
                    Console.WriteLine( serial.Error.ToString() );
                #endif

                WriteError( "Error reading information about serial. Titles for episodes will not be available." );
            }
            Exit();

            // pojistka, pokud jeste neskoncilo nacitani souboru (nemelo by se stavat)
            while( _bThread.IsAlive )
                MakeProgress();
            _bThread.Join( 1 );

            if (builder.Status != FileListBuilderStatus.BuildingFinished || builder.FoundFiles.Count == 0)
            {
                WriteError( "No files to process were found" );
                Exit( ExitCodes.NothingToDo );
            }

            builder.PrepareMove( serial.Episodes );

            if( builder.FilesForMove.Count == 0 )
                Console.WriteLine( "All files has right names, nothing to be renamed." );
            else
            {
                Console.WriteLine( "Will rename {0} files. This operation cannot be reverted.", builder.FilesForMove.Count );
                Console.WriteLine( "[Y]es do it, [N]o way !, [D]etail information" );
                while( true )
                {
                    Console.TreatControlCAsInput = true;
                    var key = Console.ReadKey( true );

                    if( key.Key == ConsoleKey.D )
                    {
                        Console.WriteLine();
                        foreach( var moveInfo in builder.FilesForMove )
                        {
                            Console.WriteLine( " <= " + moveInfo.Key.FullName );
                            Console.WriteLine( " => " + moveInfo.Value );
                            Console.WriteLine();
                        }
                        Console.WriteLine( "So what now, can i do it ? Please..." );
                        Console.WriteLine( "[Y]es do it, [N]o way !" );
                        continue;
                    }

                    if( key.Key == ConsoleKey.N )
                    {
                        Console.WriteLine( "Allright, i don't touch that." );
                        break;
                    }
                    else if( key.Key == ConsoleKey.Y )
                    {
                        Console.WriteLine( "You had been warned, processing..." );
                        builder.ProcessMove();
                        Console.WriteLine( "All files has been successfully renamed." );
                        break;
                    }
                    else
                        continue;
                }
            }

            /*
            if(  unknown.Count > 0 )
            {
                var unknownPath = Path.Combine( SerialDirectory.FullName, "!unknown" );
                Console.WriteLine();
                Console.WriteLine( "Bylo nalezeno {0} neznámých souborů. Chceš je přesunout do adresáře {1} ?", unknown.Count, unknownPath );
                Console.WriteLine( "[A]no ouhlasím, [N]echci to, [D]etailní informace" );
                Console.TreatControlCAsInput = true;
                var key = Console.ReadKey( true );
                unknown.ForEach(
                    f =>
                    {
                        if( !Directory.Exists( unknownPath ) )
                            Directory.CreateDirectory( unknownPath );
                    }
                );
            }

            if( noMatch.Count > 0 )
            {
                WriteError( "Následující soubory mají neznámý formát názvu:" );
                foreach( var noMatchItem in noMatch )
                    Console.WriteLine( noMatchItem );
            }*/

            Exit(ExitCodes.Success);
        }

        /// <summary>
        /// Handler pro zpracovani neosetrenych vyjimek
        /// </summary>
        static void UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
 	        Console.WriteLine("Caught an unhandled exception.");
            #if DEBUG
            Console.WriteLine(e.ExceptionObject.ToString());
            #endif
            if( e.IsTerminating )
            {
                /*
                if( _bThread.IsAlive )
                {
                    _bThread.Abort();
                    _bThread.Join( 1 );
                }*/
                if( _sThread.IsAlive )
                {
                    _sThread.Abort();
                    _sThread.Join( 1 );
                }
                Exit();
            }
        }

        /// <summary>
        /// Vykresli postup ve zpracovanich dlouheho procesu
        /// Pote pocka 500 milisekund
        /// </summary>
        public static void MakeProgress()
        {
            MakeProgress( 500 );
        }

        /// <summary>
        /// Vykresli postup ve zpracovanich dlouheho procesu
        /// Pote pocka stanoveny pocet milisekund
        /// </summary>
        public static void MakeProgress( int miliseconds )
        {
            Console.Write( "." );
            Thread.Sleep( miliseconds );
        }

        /// <summary>
        /// Ukoncit aplikaci s neznamym chybovym kodem
        /// </summary>
        public static void Exit()
        {
            Exit(ExitCodes.UnknownError);
        }

        /// <summary>
        /// Ukoncit aplikaci se zadanym kodem
        /// </summary>
        /// <param name="code"></param>
        public static void Exit(ExitCodes code)
        {
            Console.WriteLine();
            Console.WriteLine( "Press any key to exit" );
            Console.ReadKey( true );
            Environment.Exit( (int)code );
        }

        /// <summary>
        /// Vypsat napovedu pokud nejsou specikovany potrebne parametery
        /// </summary>
        private static void WriteHelp()
        {
            Console.WriteLine( "Supply name of subdirectory with serial as parameter" );
            Exit( ExitCodes.MissingArguments );
        }

        /// <summary>
        /// Vypsat chybu, ale pokracovat dal
        /// </summary>
        private static void WriteError( string message, params object[] args )
        {
            if( !String.IsNullOrEmpty( message ) )
                Console.WriteLine( "Error: " + String.Format( message, args ?? new object[ 0 ] ) );
        }

        /// <summary>
        /// Vypsal chybu a ukoncit aplikaci
        /// </summary>
        private static void WriteFatalError( string message, params object[] args )
        {
            WriteError( message, args );
            Exit();
        }
    }
}
